P-2171 Add Solana address validation and support#25
Conversation
Address validation in identify() and connect() previously rejected any non-EVM address because isValidAddress only matched the 0x-prefixed hex format. This blocked Solana wallets from being tracked. Add a Solana validator module mirroring the web SDK and a chain-aware validateAddress helper that: - returns checksummed EVM addresses for EVM chain IDs (or no chainId) - returns Base58 Solana addresses as-is for Solana chain IDs - falls back from EVM to Solana when no chainId is supplied isBlockedAddress now also blocks Solana system program addresses while keeping the existing EVM zero/dead address checks.
There was a problem hiding this comment.
Code Review
This pull request introduces Solana support to the analytics SDK by adding dedicated address validation utilities, type definitions, and cluster mappings. The core address validation logic has been refactored into a unified validateAddress function that handles both EVM (checksummed) and Solana (Base58) formats, with support for strict validation via chainId. Feedback suggests several improvements: removing a redundant undefined check in the Base58 validation loop, simplifying null/undefined checks using loose equality, and streamlining logic by leveraging existing utility functions like getValidSolanaAddress and isValidAddress for better consistency.
| function isValidBase58String(str: string): boolean { | ||
| for (let i = 0; i < str.length; i++) { | ||
| const ch = str[i]; | ||
| if (ch === undefined || !BASE58_CHAR_SET.has(ch)) { |
| chainId?: number | null | ||
| ): string | undefined { | ||
| // Explicit Solana chainId → validate ONLY as Solana | ||
| if (chainId !== undefined && chainId !== null && isSolanaChainId(chainId)) { |
There was a problem hiding this comment.
This check can be simplified using the loose inequality operator (!= null) to check for both null and undefined. This pattern is also applicable to the check on line 110.
| if (chainId !== undefined && chainId !== null && isSolanaChainId(chainId)) { | |
| if (chainId != null && isSolanaChainId(chainId)) { |
| if (isSolanaAddress(address)) { | ||
| return getValidSolanaAddress(address) || undefined; | ||
| } | ||
|
|
||
| return undefined; |
There was a problem hiding this comment.
| if (normalized.startsWith("0x") && normalized.length === 42) { | ||
| return BLOCKED_ADDRESSES.has(normalized); | ||
| } |
There was a problem hiding this comment.
Instead of manually checking the prefix and length, leverage the existing isValidAddress utility. This ensures consistency with the EVM address validation logic used elsewhere in the SDK.
| if (normalized.startsWith("0x") && normalized.length === 42) { | |
| return BLOCKED_ADDRESSES.has(normalized); | |
| } | |
| if (isValidAddress(trimmed)) { | |
| return BLOCKED_ADDRESSES.has(normalized); | |
| } |
- Use for...of in isValidBase58String to drop the redundant ch === undefined branch. - Drop the redundant isSolanaAddress check before getValidSolanaAddress in the no-chainId fallback (getValidSolanaAddress already validates). - Use isValidAddress in isBlockedAddress instead of duplicating the 0x-prefix/length check. Kept !== undefined && !== null over != null to match the surrounding codebase style (FormoAnalytics.ts, EventFactory.ts).
Summary
This PR adds comprehensive Solana address validation and support to the FormoAnalytics SDK, enabling it to handle both EVM and Solana blockchain addresses. The implementation includes Base58 validation, system address detection, and chain-aware address normalization.
Key Changes
New Solana address validation module (
src/solana/address.ts):isSolanaAddress(): Validates Base58 format and length (32-44 characters)getValidSolanaAddress(): Handles string and PublicKey-like objects withtoBase58()methodisSolanaSystemAddress()/isBlockedSolanaAddress(): Identifies system programs and blocked addressesSOLANA_SYSTEM_ADDRESSESconstant with well-known program addressesNew Solana types module (
src/solana/types.ts):SolanaClustertype for network identificationSOLANA_CHAIN_IDSmapping (900001-900004) to distinguish Solana networks from EVMisSolanaChainId()utility for chain identificationSolanaPublicKeyinterface for type-safe PublicKey handlingEnhanced address validation (
src/utils/address.ts):validateAddress()function with chain-aware logic:isBlockedAddress()to handle both EVM (zero/dead addresses) and Solana (system programs)validateAndChecksumAddress()helper for EVM-specific validationUpdated FormoAnalytics integration (
src/FormoAnalytics.ts):validateAndChecksumAddress()to use newvalidateAddress()functionchainIdparameter for chain-aware validationUpdated EventFactory (
src/lib/event/EventFactory.ts):validateAddress()functionComprehensive test coverage (
src/__tests__/solana.test.ts):validateAddress()with both EVM and Solana addressesisBlockedAddress()with both address typesImplementation Details
https://claude.ai/code/session_01TvJLuQ7jRicEnrjzPjY2ss